/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "../guidefaults.h"
#include "../List.h"

namespace gui {

List::List(void)
:	Component(), 
	iVisibleEntries(-1),	
	iNumbOfEntries(0),	
	iStartElement(0),
	iScrollSteps(1),
	bConstrainWidth(false),
	pButtonUp(NULL),
	pButtonDown(NULL),
	m_lastZ(0),
	m_iMarkedItem(-1),
	m_vMarkedColor(1,0,0,1)
{
	strings.clear();
}

List::~List(void)
{
	strings.clear();
}

void List::addString(const std::string& str)
{
	strings.push_back(str);
	++iNumbOfEntries;
	updateDimension();

	if (!bConstrainWidth) {
		// Get the text length of the string!
		int length = font->getLength(str);
		vDim.x = max(vDim.x, length);
	}
}

void List::updateDimension()
{
	if (iVisibleEntries == -1)
		vDim.y = iNumbOfEntries * iRowSize;
	else
		vDim.y = (iVisibleEntries + 2) * iRowSize;
}

void List::removeString(const std::string& str)
{
	static gui_list::iterator it;

	for (it=strings.begin(); it!=strings.end(); ++it) {
		if ( (*it).compare(str) == 0) {
			strings.erase(it);
			--iNumbOfEntries;
			updateDimension();
			return;
		}
	}
}

bool List::removeSelected()
{
	if (m_iIdSelected >= 0) {
		removeString( strings.at(m_iIdSelected) );
		m_iIdSelected = -1;
		return true;
	}
	return false;
}

void List::setDimension(int x, int y, int w, int h)
{
	Component::setDimension(x, y, w, vDim.y); 
}

void List::setNumberOfVisibleEntries(int numbOfEntries)
{
	iVisibleEntries = numbOfEntries;
	updateDimension();
}

void List::setScrollSteps(int _steps)
{
	iScrollSteps = _steps;
}

void List::setSelectedItem(const std::string& item)
{
	gui_list::iterator it;

	int counter = 0;
	for (it=strings.begin(); it!=strings.end(); ++it) {
		if ( (*it).compare(item) == 0)
			m_iIdSelected = counter;
		++counter;
	}
}

std::string List::getSelectedItem() const
{
	if (m_iIdSelected >= 0 && strings.size() > 0)
		return strings.at(m_iIdSelected);

	return "";
}

int List::getNumItems() const
{
	return static_cast<int>( strings.size() );
}

bool List::processMouse(int _x, int _y, int _z, int _button)
{
	if (Component::processMouse(_x, _y, _z, _button)) {
		// List not limited in the visible rows: Highlight directly
		if (iVisibleEntries == -1) {
			setHighlightedIndex( (_y - vRealPos.y) / iRowSize );
			Component::ExecUpdate();
			return true;
		} else {
				
			// List is limited: Offset of scroll bars is ommitted
			if ((_y-vRealPos.y)>iRowSize && (_y-vRealPos.y)<(iRowSize*(iVisibleEntries+1))) {
				processMouseWheel(_z);
				setHighlightedIndex( iStartElement + (_y - vRealPos.y-iRowSize) / iRowSize );				
				Component::ExecUpdate();				
				return true;
			} else {
				processMouseWheel(_z);
				setHighlightedIndex(-1);				
				Component::ExecUpdate();
			}			
		}
	} else {
		setHighlightedIndex(-1);
	}

	return false;
}

void List::processMouseWheel(int z) {
	int diff = z - m_lastZ;

	if (diff > 0) {
		iStartElement -= 3;
		iStartElement = max(0, iStartElement);
	}
	if (diff < 0) {
		iStartElement += 3;
		// Don't scroll over last element
		int offset = iNumbOfEntries - iStartElement - iVisibleEntries;
		if (offset < 0) {
			// offset is negative. Either set to 0 or to the last selected row
			iStartElement = max(0, iNumbOfEntries-iVisibleEntries);
		}
	}

	m_lastZ = z;
}

void List::clear()
{
	strings.clear();
	iNumbOfEntries = 0;
	m_iIdSelected = -1;
	iStartElement = 0;
	m_iMarkedItem = -1;
	Component::ExecUpdate();
}

void List::draw()
{
	gui_list::iterator str;
	int i, rowNum;

	if ( bVisible ) {
		vRealPos = getAbsolutePosition();
		pRenderer->drawFilledRect(vRealPos.x , vRealPos.y, vDim.x, vDim.y, vBackColor);
		pRenderer->drawRect(vRealPos.x , vRealPos.y, vDim.x, vDim.y, vBorderColor);

		if (iVisibleEntries == -1) {
			i=0;	
			for (str = strings.begin(); str != strings.end(); ++str) {
				if (isSelectable() && i == m_iIdSelected) {
					pRenderer->drawFilledRect(vRealPos.x, vRealPos.y + (iRowSize*i), vDim.x, iRowSize, vSelectedBackColor);
					pRenderer->drawRect(vRealPos.x, vRealPos.y + (iRowSize*i), vDim.x, iRowSize, vSelectedBorderColor);
					//font->setColor(vSelectedTextColor);
				}
				//else 
				//	font->setColor(vTextColor);
			
				font->print(*str, vRealPos.x+vOffset.x, vRealPos.y + (iRowSize*i)+vOffset.y, 0, FONT_LEFT, 0, 0, 
							((isSelectable() && i == m_iIdSelected) ? vSelectedTextColor : vTextColor));
				++i;
			}			
		}
		else {
			if (pButtonUp) {
				static int offUpX = (vDim.x - pButtonUp->getWidth())/2;
				pButtonUp->display(vRealPos.x + offUpX, vRealPos.y+vOffset.y);
			} else {
				font->print("^", vRealPos.x+vOffset.x, vRealPos.y+vOffset.y, 0, FONT_CENTER, vDim.x, iRowSize, 
							(iStartElement==0 ? math::Vector4(1,1,1, .4f) : vTextColor));
			}
			str = strings.begin() + iStartElement;
			rowNum=1;
			for (int i = iStartElement; (i < iNumbOfEntries) && (i < (iStartElement+iVisibleEntries)); ++i) {
				if (isSelectable() && i == m_iIdSelected) {
					pRenderer->drawFilledRect(vRealPos.x, vRealPos.y + (iRowSize*rowNum), vDim.x, iRowSize, vSelectedBackColor);
					pRenderer->drawRect(vRealPos.x, vRealPos.y + (iRowSize*rowNum), vDim.x, iRowSize, vSelectedBorderColor);
				}

				if ( i == m_iMarkedItem){
				font->print(*str, vRealPos.x+vOffset.x, vRealPos.y + (iRowSize*rowNum++)+vOffset.y, 0, FONT_LEFT, 0, 0, 
					math::Vector4( 1,0,0,1));
				}
				else{

				font->print(*str, vRealPos.x+vOffset.x, vRealPos.y + (iRowSize*rowNum++)+vOffset.y, 0, FONT_LEFT, 0, 0, 
							((isSelectable() && i == m_iIdSelected) ? vSelectedTextColor : vTextColor));
				}

				++str;
			}
			if (pButtonDown) {
				static int offDownX = (vDim.x - pButtonUp->getWidth())/2;
				pButtonDown->display(vRealPos.x + offDownX, vRealPos.y + (iRowSize*(iVisibleEntries+1)));
			} else {
				font->print("v", vRealPos.x , vRealPos.y + (iRowSize*(iVisibleEntries+1)), 0, FONT_CENTER, vDim.x, iRowSize,
							( (iStartElement+iVisibleEntries == iNumbOfEntries) ? math::Vector4(1,1,1,.4f) : vTextColor) );
			}
		}
		// Draw the hightlight if active
		if ( isHighlighted() ) 
			drawHighlight();
	}
}	

void List::drawHighlight()
{
	int highlighted;
	vRealPos = getAbsolutePosition();

	if (strings.size() == 0) 
		return;

	if (isHighlighted() && getHighlightedIndex() >= 0 && getHighlightedIndex() < (int)strings.size()) {
		highlighted = getHighlightedIndex();
		if (iVisibleEntries == -1) {
			pRenderer->drawFilledRect(vRealPos.x, vRealPos.y + (iRowSize*highlighted), vDim.x, iRowSize, vHighlightBackColor);
			pRenderer->drawRect(vRealPos.x, vRealPos.y + (iRowSize*highlighted), vDim.x, iRowSize, vHighlightBorderColor);
			std::string str;		
			str = strings[highlighted];
			font->print(str, vRealPos.x+vOffset.x, vRealPos.y + (iRowSize*highlighted)+vOffset.y, 0, FONT_LEFT, 0, 0, vHighlightTextColor);
		} else {
			pRenderer->drawFilledRect(vRealPos.x, vRealPos.y + (iRowSize*(highlighted+1-iStartElement)), vDim.x, iRowSize, vHighlightBackColor);
			pRenderer->drawRect(vRealPos.x, vRealPos.y + (iRowSize*(highlighted+1-iStartElement)), vDim.x, iRowSize, vHighlightBorderColor);
			std::string str;
			str = strings[highlighted];
			font->print(str, vRealPos.x+vOffset.x, vRealPos.y + (iRowSize*(highlighted+1-iStartElement))+vOffset.y, 0, FONT_LEFT, 0, 0, vHighlightTextColor);
		}
	}
}

void List::onMouseEntered(const MouseEvent &me)
{
	setHighlighted(true);
	Component::onMouseEntered(me);
}

void List::onMouseLeft(const MouseEvent &me)
{
	setHighlighted(false);
	Component::onMouseLeft(me);
}

void List::onMouseReleased(const MouseEvent &me)
{
	// Just select element because nothing is hidden and there are no scroll buttons
	if (iVisibleEntries == -1) {
		
		m_iIdSelected = (me.y - vRealPos.y) / iRowSize;
		if (m_iIdSelected >= iNumbOfEntries) m_iIdSelected = iNumbOfEntries-1;
		if (m_iIdSelected != -1)
			valueChanged(this);
	
	// List with hidden entries: Scroll or select?
	} else {
		// Do scrolling
		if ((me.y - vRealPos.y) < iRowSize) {
			
			iStartElement -= iScrollSteps;
			
			if (iStartElement < 0) 
				iStartElement = 0;
		
		} else if ((me.y-vRealPos.y) > (iRowSize * (iVisibleEntries + 1))) {
			
			iStartElement += iScrollSteps;
			
			// Don't scroll over last element
			int offset = iNumbOfEntries - iStartElement - iVisibleEntries;
			if (offset < 0) {
				// offset is negative. Either set to 0 or to the last selected row
				iStartElement = max(0, iNumbOfEntries-iVisibleEntries);
			}
		
		// Select element
		} else {
			m_iIdSelected = iStartElement + (me.y - vRealPos.y - iRowSize) / iRowSize;
			if (m_iIdSelected >= iNumbOfEntries) m_iIdSelected = iNumbOfEntries-1;
			if (m_iIdSelected != -1)
				valueChanged(this);
		}
	}
	Component::onMouseReleased(me);
}

void List::constrainWidth(bool val)
{
	bConstrainWidth = val;
}

void List::setButtonUp( ITexture *texture)
{
	pButtonUp = texture;
}

void List::setButtonDown( ITexture *texture)
{
	pButtonDown = texture;
}

int List::getMarkedItem(){
	return m_iMarkedItem;
}

void List::setMarkedItem(int index){
	m_iMarkedItem = index;
}


} // namespace
